package com.ltmonitor.dao.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Projections;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.ltmonitor.dao.IBaseHibernateDao;
import com.ltmonitor.entity.TenantEntity;

/**
 * 基础工具类Dao, 封装了增删改查的通用方法
 * @author www.jt808.com
 *
 * @param <K> 实体类的主键类型， 一般是Integer类型
 * @param <T> 实体类的class
 */
@Repository("baseDao")
public abstract class BaseHibernateDao<K extends Serializable, T extends Serializable>
		implements IBaseHibernateDao<K, T> {
	@Autowired
	private SessionFactory sessionFactory; // 从容器中注入session工厂【无需get,set方法】

	private Class<T> clatt; // 【实体类对应的Class对象】

	/**
	 * //向子类暴露的接口获用来获取sessionFactory
	 * 
	 * @return sessionFactory
	 */
	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}
	public BaseHibernateDao()
	{
		//ParameterizedType parameterizedType = (ParameterizedType)this.getClass().getGenericSuperclass(); 

		//clatt= (Class<T>)(parameterizedType.getActualTypeArguments()[0]); 
		
	}
	
	

	/**
	 * 保留指定clatt值的接口【通过子类显示调用父类的构造函数来指定】
	 * 
	 * @param clatt
	 */
	public BaseHibernateDao(Class<T> clatt) {
		this.clatt = clatt;
	}

	// @SuppressWarnings("unchecked")
	// public BaseDaoImpl() {//另外一种方式指定clatt值，要求类必须是abstract类型
	// ParameterizedType parameterizedType =
	// (ParameterizedType)this.getClass().getGenericSuperclass();
	// clatt= (Class<T>)(parameterizedType.getActualTypeArguments()[0]);
	// }

	@Override
	public Session getSession() {
		return getSessionFactory().getCurrentSession();
	}

	@SuppressWarnings("unchecked")
	@Override
	public K save(T t) {
		Session session = getSession();
		return (K) session.save(t);
	}

	@Override
	public T findById(K id) {
		Session session = getSession();
		@SuppressWarnings("unchecked")
		T t = (T) session.get(clatt, id);
		return t;
	}
	

	@Override
	public T find(String hsql) {
		List<T> results = query(hsql);
		if (results != null && results.size() > 0) {
			return results.get(0);
		}
		return null;
	}

	@Override
	public T find(String hsql, Object value) {
		List<T> results = query(hsql, value);
		if (results != null && results.size() > 0) {
			return results.get(0);
		}
		return null;
	}

	@Override
	public T find(String hsql, Object[] values) {
		List<T> results = query(hsql, values);
		if (results != null && results.size() > 0) {
			return results.get(0);
		}
		return null;
	}
	
	
	@Override
	public List<T> query(final String hql) {
		Session session = getSession();
		Query query=session.createQuery(hql);  
        List<T> list=query.list();  
        return list;
	}
	

	@Override
	public List<T> queryByNamedParam(final String hql, String paramName, final Object value) {
		Session session = getSession();
		Query query=session.createQuery(hql);  
		query.setParameter(paramName, value);
        List<T> list=query.list();  
        return list;
	}
	
	@Override
	public List<T> queryByNamedParam(final String hql, String paramName, final Object[] value) {
		Session session = getSession();
		Query query=session.createQuery(hql);  
		query.setParameterList(paramName, value);
        List<T> list=query.list();  
        return list;
	}
	

	@Override
	public List<T> query(final String hql, final Object[] values) {

		Session session = getSession();
		Query query=session.createQuery(hql);  
		if(values != null && values.length > 0)
		{
			for(int m = 0; m < values.length; m++)
			{
				query.setParameter(m, values[m]);
			}
		}

        List<T> list=query.list();  
        return list;
		
	}
	
	@Override
	@Transactional(readOnly = true)
	public List<T> query(final String hql, final Object value) {
		return this.query(hql,new Object[]{value});
		
	}
	

	@Override
	public void saveOrUpdateAll(Collection<T> ct) {
		Session session = getSession();
		for (T t : ct) {
			session.saveOrUpdate(t);
		}
	}

	@Override
	public T update(T t) {
		Session session = getSession();
		session.update(t);
		return t;
	}

	@Override
	public void deleteAll(Collection<T> ct) {
		Session session = getSession();
		for (T t : ct) {
			session.delete(t);
		}
	}

	@Override
	public T saveOrUpdate(T t) {
		Session session = getSession();
		session.saveOrUpdate(t);
		return t;
	}

	@Override
	public void delete(T t) {
		Session session = getSession();
		session.delete(t);
	}

	@Override
	public boolean deleteById(K id) {
		Session session = getSession();
		@SuppressWarnings("unchecked")
		T t = (T) session.get(clatt, id);
		if (t == null)
			return false;
		session.delete(t);
		return true;
	}
	
	@Override
	public void deleteFake(T t) {
		TenantEntity obj = (TenantEntity)t;
		obj.setDeleted(true);
		this.saveOrUpdate(t);
	}

	@Override
	public boolean deleteFakeById(K id) {
		Session session = getSession();
		T t = (T) session.get(clatt, id);
		if (t == null)
			return false;
		deleteFake(t);
		return true;
	}
	


	@SuppressWarnings("unchecked")
	@Override
	public QueryResult<T> load(int page, int rows) {
		Session session = getSession();
		Criteria criteria = session.createCriteria(clatt);
		criteria.setFirstResult((page - 1) * rows);
		criteria.setMaxResults(rows);
		QueryResult<T> result = new QueryResult<>();
		result.setDatas(criteria.list());
		result.setTotalCount(Long.parseLong(criteria
				.setProjection(Projections.rowCount()).uniqueResult()
				.toString()));
		return result;
	}

	@SuppressWarnings("unchecked")
	@Override
	@Transactional(readOnly = true)
	public Collection<T> loadAll() {
		Session session = getSession();
		Criteria criteria = session.createCriteria(clatt);
		
		return criteria.list();
	}

	@Override
	public long getTotalCount() {
		Session session = getSession();
		Criteria criteria = session.createCriteria(clatt);
		Object object = criteria.setProjection(Projections.rowCount())
				.uniqueResult();
		long totalCount = 0;
		if (object != null) {
			totalCount = Long.parseLong(object.toString());
		}
		return totalCount;
	}
	
	public void bulkUpdate(String hql, Object[] values)
	{
		Session session = getSession();
		Query query=session.createQuery(hql);  
		if(values != null && values.length > 0)
		{
			for(int m = 0; m < values.length; m++)
			{
				query.setParameter(m, values[m]);
			}
		}
		
		query.executeUpdate();

	}

	/****************************** HQL ******************************/
	@Override
	public QueryResult<T> getScrollData() {
		return getScrollData(-1, -1, null, null, null);
	}

	@Override
	public QueryResult<T> getScrollData(int firstResult, int maxResult) {
		return getScrollData(firstResult, maxResult, null, null, null);
	}

	@Override
	public QueryResult<T> getScrollData(int firstResult, int maxResult,
			LinkedHashMap<String, String> orderby) {
		return getScrollData(firstResult, maxResult, null, null, orderby);
	}

	@Override
	public QueryResult<T> getScrollData(int firstResult, int maxResult,
			String where, Object[] params) {
		return getScrollData(firstResult, maxResult, where, params, null);
	}

	@Override
	@SuppressWarnings("unchecked")
	public QueryResult<T> getScrollData(int firstResult, int maxResult,
			String where, Object[] params, LinkedHashMap<String, String> orderby) {
		String entityName = clatt.getSimpleName();
		String whereql = where != null && !"".equals(where.trim()) ? " where "
				+ where : "";
		Session session = getSession();
		Query query = session.createQuery("select o from " + entityName + " o"
				+ whereql + buildOrderby(orderby));
		if (firstResult != -1 && maxResult != -1)
			query.setFirstResult(firstResult).setMaxResults(maxResult);
		setQueryParameter(query, params);

		QueryResult<T> qr = new QueryResult<T>();
		// qr.setResultlist(query.getResultList());
		Query queryCount = session.createQuery("select count(o) from "
				+ entityName + " o" + whereql);
		setQueryParameter(queryCount, params);
		long count = (Long) queryCount.uniqueResult();
		qr.setTotalCount(count);
		qr.setDatas(query.list());
		return qr;
	}

	/**
	 * 设置查询参数
	 * 
	 * @param query
	 *            查询对象
	 * @param params
	 *            参数值
	 */
	public static void setQueryParameter(Query query, Object[] params) {
		if (params != null) {
			for (int i = 0; i < params.length; i++) {
				query.setParameter(i, params[i]);
			}
		}
	}

	/**
	 * 构建排序语句
	 * 
	 * @param orderby
	 *            排序属性与asc/desc, Key为属性,Value为asc/desc
	 * @return
	 */
	public static String buildOrderby(LinkedHashMap<String, String> orderby) {
		StringBuilder sb = new StringBuilder();
		if (orderby != null && !orderby.isEmpty()) {
			sb.append(" order by ");
			for (Map.Entry<String, String> entry : orderby.entrySet()) {
				sb.append("o.").append(entry.getKey()).append(" ")
						.append(entry.getValue()).append(',');
			}
			sb.deleteCharAt(sb.length() - 1);
		}
		return sb.toString();
	}

}
